package com.wfm.files;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;


public class FileCopyLineByLine {
    public static void main(String[] args) throws FileNotFoundException {
        String inputFilePath = "input.txt";  // 输入文件的路径
        String outputFilePath ="output.txt";  // 输出文件的路径

        long start = System.currentTimeMillis();
//        copy1(inputFilePath, outputFilePath);
        copy4(inputFilePath, outputFilePath);
        long end = System.currentTimeMillis();
        System.out.print("复制文件耗费毫秒："+ (end-start));
    }

    private static void copy1(String inputFilePath, String outputFilePath) {
        try (BufferedReader reader = new BufferedReader(new FileReader(inputFilePath));
             BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath))) {

            String line;
            while ((line = reader.readLine())!= null) {  // 逐行读取输入文件
                line = line.replaceAll(",", "#");
                writer.write(line);  // 将读取的行写入输出文件
                writer.newLine();  // 写入新行符，确保输出文件中的行分隔符
            }
        } catch (IOException e) {
            e.printStackTrace();  // 处理可能出现的异常
        }
    }


    // 用mmap优化copy1方法
    private static void copy2(String inputFilePath, String outputFilePath) {
        try (RandomAccessFile inputFile = new RandomAccessFile(inputFilePath, "r");
             RandomAccessFile outputFile = new RandomAccessFile(outputFilePath, "rw");
             FileChannel inputChannel = inputFile.getChannel();
             FileChannel outputChannel = outputFile.getChannel()) {

            long fileSize = inputChannel.size();
            MappedByteBuffer inputBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
            MappedByteBuffer outputBuffer = outputChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

            // 将输入缓冲区的数据复制到输出缓冲区
            for (int i = 0; i < fileSize; i++) {
                outputBuffer.put(inputBuffer.get());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 直接批量复制，而不是一个字节一个字节复制
    private static void copy3(String inputFilePath, String outputFilePath) {
        try (RandomAccessFile inputFile = new RandomAccessFile(inputFilePath, "r");
             RandomAccessFile outputFile = new RandomAccessFile(outputFilePath, "rw");
             FileChannel inputChannel = inputFile.getChannel();
             FileChannel outputChannel = outputFile.getChannel()) {

            long fileSize = inputChannel.size();
            MappedByteBuffer inputBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
            MappedByteBuffer outputBuffer = outputChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

            // 批量复制
            outputBuffer.put(inputBuffer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void copy4(String inputFilePath, String outputFilePath) {
        try (RandomAccessFile inputFile = new RandomAccessFile(inputFilePath, "r");
             RandomAccessFile outputFile = new RandomAccessFile(outputFilePath, "rw");
             FileChannel inputChannel = inputFile.getChannel();
             FileChannel outputChannel = outputFile.getChannel()) {

            long fileSize = inputChannel.size();
            MappedByteBuffer inputBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
            MappedByteBuffer outputBuffer = outputChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

            // 统计文件行数
            List<Long> lineOffsets = new ArrayList<>();
            long offset = 0;
            while (inputBuffer.hasRemaining()) {
                if (inputBuffer.get() == '\n') {
                    lineOffsets.add(offset);
                }
                offset++;
            }
            inputBuffer.rewind(); // 重置缓冲区位置

            int LINES_PER_THREAD = 2000; // 每线程处理1000行 LINES_PER_THREAD
            // 计算线程数量
            int threadCount = (int) Math.ceil((double) lineOffsets.size() / LINES_PER_THREAD);

            // 创建线程池
            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
            List<Future<List<String>>> futures = new ArrayList<>();

            // 提交任务到线程池
            for (int i = 0; i < threadCount; i++) {
                int startIndex = i * LINES_PER_THREAD;
                int endIndex = Math.min(startIndex + LINES_PER_THREAD, lineOffsets.size());
                futures.add(executorService.submit(new Callable<List<String>>() {
                    @Override
                    public List<String> call() throws Exception {
                        List<String> lines = new ArrayList<>();
                        for (int i = startIndex; i < endIndex; i++) {
                            long startOffset = (i == 0) ? 0 : lineOffsets.get(i - 1) + 1;
                            long endOffset = lineOffsets.get(i);
                            int length = (int) (endOffset - startOffset);

                            byte[] lineBytes = new byte[length];
                            inputBuffer.position((int) startOffset);
                            inputBuffer.get(lineBytes);

                            String line = new String(lineBytes, StandardCharsets.UTF_8);
                            lines.add(line);
                        }
                        return lines;
                    }
                }));
            }

            // 收集任务结果并写入输出文件
            for (Future<List<String>> future : futures) {
                List<String> lines = future.get();
                for (String line : lines) {
                    byte[] bytes = (line + "\n").getBytes(StandardCharsets.UTF_8);
                    outputChannel.write(java.nio.ByteBuffer.wrap(bytes));
                }
            }

            // 关闭线程池
            executorService.shutdown();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    // https://mp.weixin.qq.com/s/QUG7bYxXTiSfKqc5J-kMXw
    // https://mp.weixin.qq.com/s/YmQBjsq6Q78_ArSYRHedrg
    // https://blog.csdn.net/weixin_44001965/article/details/143759927



}